package com.helloaps.scheduling.service.impl;

import com.helloaps.scheduling.domain.Job;
import com.helloaps.scheduling.domain.ProcessStep;
import com.helloaps.scheduling.domain.ProductionSchedule;
import com.helloaps.scheduling.domain.WorkCalendar;
import com.helloaps.scheduling.service.IDemoDataGenerator;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.*;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

@Service
public class DemoDataGeneratorImpl implements IDemoDataGenerator {
    private static ProductionSchedule solution = new ProductionSchedule();

    @Override
    public ProductionSchedule getDemoData() {
        return solution;
    }

    @Override
    public void setDemoData(ProductionSchedule schedule) {
        solution = schedule;
    }

    /**
     * 初始化时生成测试数据，懒着落库了，每次服务启动都随机跑一波吧
     */
    @PostConstruct
    public void init() {
        /*
         * 测试数据的量级
         * 工序数
         * 工单数（这里是派到每道工序级的工单）
         */
        int lineCount = 5;
        int jobCount = 100;

        // 定义一个 job duration 的区间，生成 job 的时候在这个区间内随机个数，单位小时
        int jobDurationHoursMinimum = 2;
        int jobDurationHoursMaximum = 30;

        // 单个 job 的平均耗时，这里假设每个工序都需要预留 5 小时的 buffer，实际应该是个变量
        int averageCleaningDurationHours = 5;
        int averageCleaningAndJobDurationHours =
                averageCleaningDurationHours + (jobDurationHoursMinimum + jobDurationHoursMaximum) / 2;

        // 取当前日期之后（包括当前日期）的第一个周一
        final LocalDate START_DATE = LocalDate.now().with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
        // 00:00:00 时间
        final LocalDateTime START_DATE_TIME = LocalDateTime.of(START_DATE, LocalTime.MIDNIGHT);
        // 把时间跨度 3 个月，要不时间太短了，排程太紧凑了
        final LocalDate END_DATE = START_DATE.plusMonths(3);
        final LocalDateTime END_DATE_TIME = LocalDateTime.of(END_DATE, LocalTime.MIDNIGHT);

        Random random = new Random(37);

        // 设定时间范围
        solution.setWorkCalendar(new WorkCalendar(START_DATE, END_DATE));

        // 测试数据，不给自己添麻烦了，放弃订单、物料、工艺路线这些数据，直接工单级工序数据开搞
        solution.setOrders(new ArrayList<>());

        // 生成工序
        DecimalFormat df = new DecimalFormat("000");
        List<ProcessStep> lines = new ArrayList<>(lineCount);
        for (int i = 0; i < lineCount; i++) {
            // 这里默认了产能都是12h，每道工序都预留1h
            lines.add(new ProcessStep(String.valueOf(i),
                    String.valueOf(i),
                    "Line " + (i + 1),
                    df.format(i+1),
                    BigDecimal.valueOf(20),
                    START_DATE_TIME,
                    Duration.ofHours(1),
                    null,
                    new ArrayList<>()));
        }
        solution.setLines(lines);

        //  生成 job
        List<Job> jobs = new ArrayList<>(jobCount);
        for (int i = 0; i < jobCount; i++) {
            Duration duration = Duration.ofHours(jobDurationHoursMinimum
                    + random.nextInt(jobDurationHoursMaximum - jobDurationHoursMinimum));
            // 这里为了避免 job 都堆积到某一天，写了个简单的公式分布了一下
            // 工序数可以简单的理解成是 job 的分组
            int targetDayIndex = (i / lineCount) * averageCleaningAndJobDurationHours / 24;
            LocalDateTime minStartTime = START_DATE.plusDays(random.nextInt(Math.max(1, targetDayIndex - 2)))
                    .atTime(LocalTime.MIDNIGHT);
            LocalDateTime idealEndTime = START_DATE.plusDays(targetDayIndex + random.nextInt(3))
                    .atTime(16, 0);
            // 最多可延期1-3天
            LocalDateTime maxEndTime = idealEndTime.plusDays(1 + random.nextInt(3));
            jobs.add(new Job(String.valueOf(i),
                    "Job " + (i + 1),
                    null,
                    duration,
                    minStartTime,
                    idealEndTime,
                    maxEndTime,
                    1,
                    false));
        }
        jobs.sort(Comparator.comparing(Job::getIdealEndTime));
        solution.setJobs(jobs);
    }

}
